#include "stdafx.h"
#include "LightShaderClass.h"


CLightShaderClass::CLightShaderClass(void) : m_pVertexShader(nullptr), m_pPixelShader(nullptr), m_pLayout(nullptr), m_pSampleState(nullptr), m_pMatrixBuffer(nullptr), m_pCameraBuffer(nullptr), m_pLightBuffer(nullptr)
{
}


CLightShaderClass::~CLightShaderClass(void)
{
}

bool CLightShaderClass::Initialize(ID3D11Device* pDevice, HWND hWnd)
{
	bool bResult = false;

	bResult = InitializeShader(pDevice, hWnd, L"Light.vs", L"Light.ps");
	if(bResult == false)
	{
		DEBUG_OUTPUT("Failed to initialize light shader");
		return false;
	}

	return true;
}

void CLightShaderClass::ShutDown()
{
	ShutDownShader();
}

bool CLightShaderClass::Render(ID3D11DeviceContext* pDeviceContext, int nIndexCount, D3DXMATRIX d3dWorld, D3DXMATRIX d3dView, D3DXMATRIX d3dProj, ID3D11ShaderResourceView* pTexture, D3DXVECTOR3 d3dLightDir, D3DXVECTOR4 d3dAmbient, D3DXVECTOR4 d3dDiffuseColor, D3DXVECTOR3 d3dCamPosition, D3DXVECTOR4 d3dSpecularColor, float fSpecularPower)
{
	bool bResult = false;

	bResult = SetShaderParameters(pDeviceContext, d3dWorld, d3dView, d3dProj, pTexture, d3dLightDir, d3dAmbient, d3dDiffuseColor, d3dCamPosition, d3dSpecularColor, fSpecularPower);
	if(bResult = false)
	{
		DEBUG_OUTPUT("Failed to set shader parameters");
		return false;
	}

	RenderShader(pDeviceContext, nIndexCount);

	return true;
}

void CLightShaderClass::RenderShader(ID3D11DeviceContext* pDeviceContext, int nIndexCount)
{
	pDeviceContext->IASetInputLayout(m_pLayout);
	pDeviceContext->VSSetShader(m_pVertexShader, NULL, 0);
	pDeviceContext->PSSetShader(m_pPixelShader, NULL, 0);
	pDeviceContext->PSSetSamplers(0, 1, &m_pSampleState);
	pDeviceContext->DrawIndexed(nIndexCount, 0, 0);
}

bool CLightShaderClass::InitializeShader(ID3D11Device* pDevice, HWND hWnd, WCHAR* pwszVSLightShaderFileName, WCHAR* pwszPSLightShaderFileName)
{
	HRESULT hResult;
	ID3D10Blob* pErrorMessage = nullptr;
	ID3D10Blob* pVertexShaderBuffer = nullptr;
	ID3D10Blob* pPixelShaderBuffer = nullptr;
	D3D11_INPUT_ELEMENT_DESC stPolygonLayout[3];
	unsigned int unNumElements;
	D3D11_SAMPLER_DESC stSamplerDesc;
	D3D11_BUFFER_DESC stMatrixBufferDesc;
	D3D11_BUFFER_DESC stCameraBufferDesc;
	D3D11_BUFFER_DESC stLightBufferDesc;

	hResult = D3DX11CompileFromFile(pwszVSLightShaderFileName, NULL, NULL, "LightVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &pVertexShaderBuffer, &pErrorMessage, NULL);
	if(FAILED(hResult))
	{
		if(pErrorMessage)
		{
			OutputShaderErrorMessage(pErrorMessage, hWnd, pwszVSLightShaderFileName);
		}
		else
		{
			DEBUG_OUTPUT("Light shader file not found");
		}

		return false;
	}

	hResult = D3DX11CompileFromFile(pwszPSLightShaderFileName, NULL, NULL, "LightPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &pPixelShaderBuffer, &pErrorMessage, NULL);
	if(FAILED(hResult))
	{
		if(pErrorMessage)
		{
			OutputShaderErrorMessage(pErrorMessage, hWnd, pwszPSLightShaderFileName);
		}
		else
		{
			DEBUG_OUTPUT("Light shader file not found");
		}

		return false;
	}

	hResult = pDevice->CreateVertexShader(pVertexShaderBuffer->GetBufferPointer(), pVertexShaderBuffer->GetBufferSize(), NULL, &m_pVertexShader);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create light vertex shader");
		return false;
	}

	hResult = pDevice->CreatePixelShader(pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), NULL, &m_pPixelShader);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create light pixel shader");
		return false;
	}

	stPolygonLayout[0].SemanticName = "POSITION";
	stPolygonLayout[0].SemanticIndex = 0;
	stPolygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	stPolygonLayout[0].InputSlot = 0;
	stPolygonLayout[0].AlignedByteOffset = 0;
	stPolygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	stPolygonLayout[0].InstanceDataStepRate = 0;
	
	stPolygonLayout[1].SemanticName = "TEXCOORD";
	stPolygonLayout[1].SemanticIndex = 0;
	stPolygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
	stPolygonLayout[1].InputSlot = 0;
	stPolygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	stPolygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	stPolygonLayout[1].InstanceDataStepRate = 0;

	stPolygonLayout[2].SemanticName = "NORMAL";
	stPolygonLayout[2].SemanticIndex = 0;
	stPolygonLayout[2].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	stPolygonLayout[2].InputSlot = 0;
	stPolygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	stPolygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	stPolygonLayout[2].InstanceDataStepRate = 0;

	unNumElements = sizeof(stPolygonLayout) / sizeof(stPolygonLayout[0]);

	hResult = pDevice->CreateInputLayout(stPolygonLayout, unNumElements, pVertexShaderBuffer->GetBufferPointer(), pVertexShaderBuffer->GetBufferSize(), &m_pLayout);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create light input layout");
		return false;
	}

	pVertexShaderBuffer->Release();
	pVertexShaderBuffer = nullptr;

	pPixelShaderBuffer->Release();
	pPixelShaderBuffer = nullptr;

	stSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	stSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	stSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	stSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	stSamplerDesc.MipLODBias = 0.0f;
	stSamplerDesc.MaxAnisotropy = 1;
	stSamplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	stSamplerDesc.BorderColor[0] = 0;
	stSamplerDesc.BorderColor[1] = 0;
	stSamplerDesc.BorderColor[2] = 0;
	stSamplerDesc.BorderColor[3] = 0;
	stSamplerDesc.MinLOD = 0;
	stSamplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

	hResult = pDevice->CreateSamplerState(&stSamplerDesc, &m_pSampleState);
	if(FAILED(hResult))
	{
		return false;
	}

	stMatrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	stMatrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
	stMatrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	stMatrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	stMatrixBufferDesc.MiscFlags = 0;
	stMatrixBufferDesc.StructureByteStride = 0;

	hResult = pDevice->CreateBuffer(&stMatrixBufferDesc, NULL, &m_pMatrixBuffer);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create light matrix buffer");
		return false;
	}

	stCameraBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	stCameraBufferDesc.ByteWidth = sizeof(CameraBufferType);
	stCameraBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	stCameraBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	stCameraBufferDesc.MiscFlags = 0;
	stCameraBufferDesc.StructureByteStride = 0;

	hResult = pDevice->CreateBuffer(&stCameraBufferDesc, NULL, &m_pCameraBuffer);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create camera buffer");
		return false;
	}

	stLightBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	stLightBufferDesc.ByteWidth = sizeof(LightBufferType);
	stLightBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	stLightBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	stLightBufferDesc.MiscFlags = 0;
	stLightBufferDesc.StructureByteStride = 0;

	hResult = pDevice->CreateBuffer(&stLightBufferDesc, NULL, &m_pLightBuffer);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create light buffer");
		return false;
	}

	return true;
}

void CLightShaderClass::ShutDownShader()
{
	if(m_pCameraBuffer)
	{
		m_pCameraBuffer->Release();
		m_pCameraBuffer = 0;
	}

	if(m_pLightBuffer)
	{
		m_pLightBuffer->Release();
		m_pLightBuffer = 0;
	}

	if(m_pMatrixBuffer)
	{
		m_pMatrixBuffer->Release();
		m_pMatrixBuffer = 0;
	}

	if(m_pSampleState)
	{
		m_pSampleState->Release();
		m_pSampleState = 0;
	}

	if(m_pLayout)
	{
		m_pLayout->Release();
		m_pLayout = 0;
	}

	if(m_pPixelShader)
	{
		m_pPixelShader->Release();
		m_pPixelShader = 0;
	}

	if(m_pVertexShader)
	{
		m_pVertexShader->Release();
		m_pVertexShader = 0;
	}
}

void CLightShaderClass::OutputShaderErrorMessage(ID3D10Blob* pErrorMessage, HWND hWnd, WCHAR* pwscShaderFileName)
{
	char* cpCompileErrors;
	unsigned long ulBufferSize;
	ofstream fout;

	cpCompileErrors = (char*)(pErrorMessage->GetBufferPointer());

	ulBufferSize = pErrorMessage->GetBufferSize();

	fout.open("shader-error.txt");

	for(unsigned long i = 0; i < ulBufferSize; ++i)
	{
		fout << cpCompileErrors[i];
	}

	fout.close();

	pErrorMessage->Release();
	pErrorMessage = 0;

	DEBUG_OUTPUT("Error compiling shader. Check shader-error.txt for message.");
}

bool CLightShaderClass::SetShaderParameters(ID3D11DeviceContext* pDeviceContext, D3DXMATRIX d3dWorld, D3DXMATRIX d3dView, D3DXMATRIX d3dProj, ID3D11ShaderResourceView* pTexture, D3DXVECTOR3 d3dLightDir, D3DXVECTOR4 d3dAmbient, D3DXVECTOR4 d3dDiffuseColor, D3DXVECTOR3 d3dCamPosition, D3DXVECTOR4 d3dSpecularColor, float fSpecularPower)
{
	HRESULT hResult;
	D3D11_MAPPED_SUBRESOURCE stMappedResource;
	unsigned int unBufferNumber;
	MatrixBufferType* pMatrixDataPtr;
	LightBufferType* pLightDataPtr;
	CameraBufferType* pCameraDataPtr;

	D3DXMatrixTranspose(&d3dWorld, &d3dWorld);
	D3DXMatrixTranspose(&d3dView, &d3dView);
	D3DXMatrixTranspose(&d3dProj, &d3dProj);

	hResult = pDeviceContext->Map(m_pMatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &stMappedResource);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to map light shader");
		return false;
	}

	pMatrixDataPtr = (MatrixBufferType*)stMappedResource.pData;

	pMatrixDataPtr->d3dWorldMatrix = d3dWorld;
	pMatrixDataPtr->d3dViewMatrix = d3dView;
	pMatrixDataPtr->d3dProjectionMatrix = d3dProj;

	pDeviceContext->Unmap(m_pMatrixBuffer, 0);

	unBufferNumber = 0;

	pDeviceContext->VSSetConstantBuffers(unBufferNumber, 1, &m_pMatrixBuffer);
	
	hResult = pDeviceContext->Map(m_pCameraBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &stMappedResource);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to map camera buffer");
		return false;
	}

	pCameraDataPtr = (CameraBufferType*)stMappedResource.pData;
	
	pCameraDataPtr->d3dCameraPosition = d3dCamPosition;
	pCameraDataPtr->padding = 0.0f;

	pDeviceContext->Unmap(m_pCameraBuffer, 0);
	
	unBufferNumber = 1;

	pDeviceContext->VSSetConstantBuffers(unBufferNumber, 1, &m_pCameraBuffer);

	pDeviceContext->PSSetShaderResources(0, 1, &pTexture);

	hResult = pDeviceContext->Map(m_pLightBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &stMappedResource);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to map light buffer");
		return false;
	}

	pLightDataPtr = (LightBufferType*)stMappedResource.pData;

	pLightDataPtr->d3dAmbientColor = d3dAmbient;
	pLightDataPtr->d3dDiffuseColor = d3dDiffuseColor;
	pLightDataPtr->d3dLightDirection = d3dLightDir;
	pLightDataPtr->d3dSpecularColor = d3dSpecularColor;
	pLightDataPtr->fSpecularPower = fSpecularPower;

	pDeviceContext->Unmap(m_pLightBuffer, 0);

	unBufferNumber = 0;

	pDeviceContext->PSSetConstantBuffers(unBufferNumber, 1, &m_pLightBuffer);

	return true;
}
